#include "backupmanager.h"
#include <QFile>
#include <QDataStream>
#include <QDebug>
#include <QCryptographicHash>


BackupManager::BackupManager(QObject *parent)
    : QObject(parent), totalFiles(0), copiedFiles(0), maxThreads(4) {}


void BackupManager::setMaxThreads(int maxThreads) {
    this->maxThreads = maxThreads;
}

/*
 * 开始备份文件
 * sourceDir 源路径
 * targetDir 目标路径
 * incremental 增量开关
 *
 */
void BackupManager::startBackup(const QString &sourceDir, const QString &targetDir, bool incremental) {
    emit progressUpdated(0);
    // 记录开始备份的日志
    emit logMessage("开始备份");
    emit logMessage("源目录: " + sourceDir);
    emit logMessage("目标目录: " + targetDir);
    QString increment = "";
    if(incremental){
        increment= "true";
    }else{
        increment= "false";
    }
    emit logMessage("增量备份: " + increment);

    //如果要创建的目录已经存在，则不会报错
    if (!createDirectoryIfNotExists(targetDir.toStdString())) {
        std::cerr << "Failed to create target directory: " << targetDir.toStdString() << std::endl;
        return;
    }

    totalFiles = calculateTotalFiles(sourceDir.toStdString());
    copiedFiles = 0;

    //拷贝目录
    copyDirectory(sourceDir.toStdString(), targetDir.toStdString(), incremental);

    emit progressUpdated(100); // 完成时进度为100%

    // 使用异步方式发送 finished 信号
    QMetaObject::invokeMethod(this, "emitFinished", Qt::QueuedConnection);
    // 记录备份完成的日志
    emit logMessage("备份任务已完成！");
}

void BackupManager::emitFinished() {
    emit finished();
}

/*
 * 拷贝文件
 * source 源文件
 * target 目标文件
 * useMultiThread 多线程使用开关
 *
 */
void BackupManager::copyFile(const std::string &source, const std::string &target, bool useMultiThread) {
    if (useMultiThread) {
        //开启多线程来执行大文件的拷贝
        int src_fd = open(source.c_str(), O_RDONLY);
        if (src_fd == -1) {
            std::cerr << "Failed to open source file: " << source << std::endl;
            return;
        }

        struct stat fileInfo;
        fstat(src_fd, &fileInfo);
        int fileSize = fileInfo.st_size;

        int threadCount = maxThreads; // 使用4个线程
        int chunkSize = (fileSize + threadCount - 1) / threadCount; // 每个线程处理的文件块大小

        pthread_t threads[threadCount];
        FileCopyArgs args[threadCount];

        for (int i = 0; i < threadCount; ++i) {
            args[i] = {source, target, i * chunkSize, (i + 1) * chunkSize - 1};
            if (pthread_create(&threads[i], nullptr, fileCopyThread, &args[i]) != 0) {
                std::cerr << "Failed to create thread: " << i << std::endl;
            }
        }

        for (int i = 0; i < threadCount; ++i) {
            pthread_join(threads[i], nullptr);
        }

        close(src_fd);
    }else{
        int src_fd = open(source.c_str(), O_RDONLY);
        if (src_fd == -1) {
            std::cerr << "Failed to open source file: " << source << " - " << strerror(errno) << std::endl;
            return;
        }

        int dst_fd = open(target.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
        if (dst_fd == -1) {
            close(src_fd);
            std::cerr << "Failed to open target file: " << target << " - " << strerror(errno) << std::endl;
            return;
        }

        char buffer[4096];
        ssize_t bytesRead;

        //边读边写
        while ((bytesRead = read(src_fd, buffer, sizeof(buffer))) > 0) {
            if (write(dst_fd, buffer, bytesRead) != bytesRead) {
                std::cerr << "Failed to write to target file: " << target << " - " << strerror(errno) << std::endl;
                close(src_fd);
                close(dst_fd);
                return;
            }
        }

        if (bytesRead == -1) {
            std::cerr << "Failed to read from source file: " << source << " - " << strerror(errno) << std::endl;
        }

        close(src_fd);
        close(dst_fd);

        ++copiedFiles;

        if(totalFiles != 0){
            int progress = (copiedFiles * 100) / totalFiles;
            emit progressUpdated(progress);
        }else{
            std::cerr << "totalDiles value is zero" << totalFiles << " - " << strerror(errno) << std::endl;
        }

    }

}

//线程入口点，根据传入的偏移量设置文件指针位置
void* BackupManager::fileCopyThread(void* arg) {
    FileCopyArgs* args = static_cast<FileCopyArgs*>(arg);

    int src_fd = open(args->source.c_str(), O_RDONLY);
    if (src_fd == -1) {
        std::cerr << "Failed to open source file in thread:" << args->source << " - " << strerror(errno) << std::endl;
        return nullptr;
    }

    int dst_fd = open(args->target.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
    if (dst_fd == -1) {
        close(src_fd);
        std::cerr << "Failed to open target file in thread:" << args->target << " - " << strerror(errno) << std::endl;
        return nullptr;
    }

    lseek(dst_fd, args->startOffset, SEEK_SET);
    lseek(src_fd, args->startOffset, SEEK_SET);

    char buffer[4096];
    ssize_t bytesRead;

    while (args->startOffset <= args->endOffset) {
        bytesRead = read(src_fd, buffer, sizeof(buffer));
        if (bytesRead > 0) {
            if (write(dst_fd, buffer, bytesRead) != bytesRead) {
                std::cerr << "Failed to write to target file in thread:" << args->target << " - " << strerror(errno) << std::endl;
                close(src_fd);
                close(dst_fd);
                return nullptr;
            }
            args->startOffset += bytesRead;
        } else if (bytesRead == 0) {
            break;
        } else {
            std::cerr << "Failed to read from source file in thread:" << args->source << " - " << strerror(errno) << std::endl;
            close(src_fd);
            close(dst_fd);
            return nullptr;
        }
    }

    close(src_fd);
    close(dst_fd);

    return nullptr;
}

/*
 * 拷贝路径
 * source 源文件
 * target 目标文件
 * incremental 增量开关
 *
 */
void BackupManager::copyDirectory(const std::string &source, const std::string &target, bool incremental) {
    //打开源文件路径
    DIR *dir = opendir(source.c_str());
    if (!dir) {
        std::cerr << "Failed to open directory: " << source << " - " << strerror(errno) << std::endl;
        return;
    }

    struct dirent *entry;
    while ((entry = readdir(dir)) != nullptr) {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue;
        }

        std::string sourceFilePath = source + "/" + entry->d_name;
        std::string targetFilePath = target + "/" + entry->d_name;

        //获取文件状态
        struct stat fileInfo = getFileStat(sourceFilePath);
        //如果是目录，则递归
        if (S_ISDIR(fileInfo.st_mode)) {
            if (!createDirectoryIfNotExists(targetFilePath)) {
                std::cerr << "Failed to create directory: " << targetFilePath << std::endl;
                continue;
            }
            copyDirectory(sourceFilePath, targetFilePath, incremental);
        } else {
            bool useMultiThread = (fileInfo.st_size > 500 * 1024 * 1024); // 判断文件大小是否大于500MB
            if (incremental) {
                struct stat targetFileInfo = getFileStat(targetFilePath);
                if (fileInfo.st_mtime > targetFileInfo.st_mtime ||
                    fileInfo.st_size != targetFileInfo.st_size ||
                    fileInfo.st_mode != targetFileInfo.st_mode ||
                    getFileChecksum(sourceFilePath) != getFileChecksum(targetFilePath)){
                    copyFile(sourceFilePath, targetFilePath, useMultiThread);
                }
            } else {
                copyFile(sourceFilePath, targetFilePath, useMultiThread);
            }
        }
    }

    closedir(dir);
    // 记录文件拷贝完成的日志
    emit logMessage("文件拷贝完成: " + QString::fromStdString(source));
}

//记录要拷贝目录下文件的总数
int BackupManager::calculateTotalFiles(const std::string &dirPath) {
    int count = 0;
    DIR *dir = opendir(dirPath.c_str());
    if (!dir) {
        std::cerr << "Failed to open directory: " << dirPath << " - " << strerror(errno) << std::endl;
        return 0;
    }

    struct dirent *entry;
    while ((entry = readdir(dir)) != nullptr) {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue;
        }

        std::string filePath = dirPath + "/" + entry->d_name;
        struct stat fileInfo = getFileStat(filePath);
        //是否表示一个常规文件
        if (S_ISREG(fileInfo.st_mode)) {
            ++count;
        } else if (S_ISDIR(fileInfo.st_mode)) {
            count += calculateTotalFiles(filePath);
        }
    }

    closedir(dir);
    return count;
}

//创建目录如果不存在
bool BackupManager::createDirectoryIfNotExists(const std::string &dirPath) {
    if (mkdir(dirPath.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1 && errno != EEXIST) {
        std::cerr << "Failed to create directory: " << dirPath << " - " << strerror(errno) << std::endl;
        return false;
    }
    return true;
}

//获取文件状态
struct stat BackupManager::getFileStat(const std::string &filePath) {
    struct stat fileInfo;
    if (stat(filePath.c_str(), &fileInfo) == -1) {
        std::cerr << "Failed to get file info: " << filePath << " - " << strerror(errno) << std::endl;
        memset(&fileInfo, 0, sizeof(fileInfo));
    }
    return fileInfo;
}

QByteArray BackupManager::getFileChecksum(const std::string &filePath){
    QFile file(QString::fromStdString(filePath));
    if(!file.open(QIODevice::ReadOnly)){
        std::cerr << "Failed to open file for checksum:" << filePath << " - " << strerror(errno) << std::endl;
        return QByteArray();
    }

    QCryptographicHash hash(QCryptographicHash::Md5);
    if (hash.addData(&file)) {
        return hash.result().toHex();
    }

    return QByteArray();
}

